package com.huanzhidadi.single.lambda;

import com.alibaba.fastjson2.JSONObject;
import com.huanzhidadi.single.lambda.model.Hero;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class LambdaTest {
    public static boolean testHero(Hero hero) {
        return hero.getHp()>100 && hero.getDamage()<50;
    }
    public boolean testHero2(Hero hero) {
        return hero.getDamage()>50;
    }
    public static void main(String[] args) {
        Random random = new Random();
        List<Hero> heroList = new ArrayList<>();
        for (int i=0; i< 10; i++) {
            Hero hero = new Hero("hero " + i, random.nextInt(200), random.nextInt(100));
            heroList.add(hero);
        }
        heroList.add(heroList.get(heroList.size()/2));
        System.out.println("初始化后的集合(有重复数据)：");
        System.out.println(heroList);
        System.out.println("使用匿名类的方式，筛选出hp>100 && damage<50的英雄");
        HeroChecker heroChecker = new HeroChecker() {
            @Override
            public boolean test(Hero hero) {
                return hero.getHp()>100 && hero.getDamage()<50;
            }
        };
        filter(heroList, heroChecker);

        System.out.println("使用lambda的方式，筛选出hp>100 && damage<50的英雄");
        HeroChecker heroChecker1 = hero -> hero.getHp()>100 && hero.getDamage()<50;
        filter(heroList, heroChecker1);

        System.out.println("直接传递lambda的方式，筛选出hp>100 && damage<50的英雄");
        filter(heroList, hero -> hero.getHp()>100 && hero.getDamage()<50);

        System.out.println("引用静态方法的方式1，筛选出hp>100 && damage<50的英雄");
        filter(heroList, hero -> LambdaTest.testHero(hero));
        System.out.println("引用静态方法的方式2，筛选出hp>100 && damage<50的英雄");
        filter(heroList, LambdaTest::testHero);
        System.out.println("引用对象方法的方式，筛选出damage>50的英雄");
        LambdaTest lambdaTest = new LambdaTest();
        filter(heroList, lambdaTest::testHero2);
        System.out.println("引用容器中的对象方法的方式，筛选出hp<100 && damage>50的英雄");
        filter(heroList, Hero::matched);
        System.out.println("使用lambda聚合的方式1，筛选出hp>100 && damage<50的英雄");
        heroList.stream().filter(hero -> hero.getHp()>100 && hero.getDamage()<50)
                .forEach(hero -> System.out.print(hero));
        System.out.println("使用lambda聚合的方式2，筛选出hp>100 && damage<50的英雄");
        heroList.stream().filter(hero -> hero.getHp()>100 && hero.getDamage()<50)
                .forEach(System.out::print);

        System.out.println("使用lambda聚合的方式，从数组中筛选出hp>100 && damage<50的英雄");
        Hero[] heroArray = heroList.toArray(new Hero[heroList.size()]);
        Arrays.stream(heroArray).filter(hero -> hero.getHp()>100 && hero.getDamage()<50)
                .forEach(hero -> System.out.print(hero));

        // 去除重复数据
        System.out.println("去除重复数据后：");
        heroList.stream().distinct().forEach(h -> System.out.print(h));

        // 降序排序
        System.out.println("降序排序后：");
        heroList.stream().sorted((o1, o2) -> o1.getDamage()<o2.getDamage()?1:-1).forEach(h -> System.out.print(h));

        // 保留5个
        System.out.println("保留5个后：");
        heroList.stream().limit(5).forEach(h -> System.out.print(h));

        // 降序排序后保留5个
        System.out.println("降序排序后保留5个：");
        heroList.stream().sorted((o1, o2) -> o1.getDamage()<o2.getDamage()?1:-1).limit(5).forEach(h -> System.out.print(h));

        // 忽略5个
        System.out.println("忽略5个后：");
        heroList.stream().skip(5).forEach(h -> System.out.print(h));

        // mapToDouble
        System.out.println("mapToDouble后：");
        heroList.stream().mapToDouble(Hero::getHp).forEach(h -> System.out.println(h));

        // 转换任意类型的stream
        System.out.println("转换任意类型的stream后：");
        heroList.stream().map(hero -> hero.getName() + "-" + hero.getHp() + "-" + hero.getDamage()).forEach(h -> System.out.println(h));

        System.out.println("使用new Function转换JSONObject后：");
        heroList.stream().map(new Function<Hero, JSONObject>() {
            @Override
            public JSONObject apply(Hero hero) {
                return JSONObject.parseObject(JSONObject.toJSONString(hero));
            }
        }).forEach(h -> System.out.println(h));

        // 字符串转对象
        System.out.println("字符串转对象后：");
        Stream.of("测试一,122,66", "小王,99,35").map(new Function<String, Hero>() {
            @Override
            public Hero apply(String s) {
                String[] strArray = s.split(",");
                Hero hero = new Hero(strArray[0], Float.valueOf(strArray[1]), Integer.valueOf(strArray[2]));
                return hero;
            }
        }).forEach(h -> System.out.print(h));

        // Stream 流提供了一个 collect() 方法，可以收集流中的数据到【集合】或者【数组】中去
        System.out.println("使用collect()收集流中数据到List后：");
        List<Hero> heroList1 = heroList.stream().sorted((o1, o2) -> o1.getHp()>o2.getHp()?-1:1)
                .limit(5).collect(Collectors.toList());
        heroList1.forEach(System.out::print);

        // (v1, v2) -> v1)避免重复数据影响，报java.lang.IllegalStateException: Duplicate key
        System.out.println("使用collect()收集流中数据到Map后：");
        Map<String, Hero> heroMap = heroList.stream().sorted((o1, o2) -> o1.getHp()>o2.getHp()?-1:1)
                .limit(5).collect(Collectors.toMap(hero -> hero.getName(), hero -> hero, (v1, v2) -> v1));
        System.out.println(heroMap);

        System.out.println("使用Collectors.maxBy筛选出攻击力最高：");
        Optional<Hero> collect = heroList.stream().collect(Collectors.maxBy(((o1, o2) -> o1.getDamage() - o2.getDamage())));
        System.out.println("攻击力最高：" + collect.get());

        System.out.println("使用Collectors.summingDouble筛选出hp总和：");
        Double sum = heroList.stream().collect(Collectors.summingDouble(Hero::getHp));
        System.out.println("hp总和：" + sum);

        System.out.println("使用Collectors.averagingDouble筛选出平均攻击：");
        Double aveDamage = heroList.stream().collect(Collectors.averagingDouble(Hero::getDamage));
        System.out.println("平均攻击：" + aveDamage);

        System.out.println("使用Collectors.groupingBy根据某个属性来将数据进行分组：");
        Map<Integer, List<Hero>> heroMap2 = heroList.stream().collect(Collectors.groupingBy(Hero::getDamage));
        System.out.println(heroMap2);

        System.out.println("使用Collectors.groupingBy根据某个属性来将数据进行分组2：");
        Map<String, List<Hero>> heroMap3 = heroList.stream().collect(Collectors.groupingBy(hero -> {
            if (hero.getDamage() >= 90) {
                return "传说";
            } else if (hero.getDamage() >= 80) {
                return "精英";
            } else if (hero.getDamage() >= 60) {
                return "优秀";
            } else {
                return "普通";
            }
        }));
        heroMap3.forEach((key, value) -> {
            System.out.println(key + "--->" + value);
        });

        // Collectors.partitioningBy() 分区操作 根据返回值是否为 true，把集合分为两个列表，一个 true 列表，一个 false 列表
        // 分组和分区的区别就在：分组可以有多个组。分区只会有两个区( true 和 false)
        System.out.println("使用Collectors.partitioningBy()根据某个属性来将数据进行分区：");
        Map<Boolean, List<Hero>> heroMap4 = heroList.stream().collect(Collectors.partitioningBy(hero -> hero.getDamage()>=60));
        heroMap4.forEach((key, value) -> {
            System.out.println(key + "--->" + value);
        });

        // Collectors.joining() 拼接
        System.out.println("使用Collectors.joining()将数据进行拼接：");
        String joining1 = heroList.stream().map(hero -> hero.getName()).collect(Collectors.joining());
        System.out.println("joining1:" + joining1);
        String joining2 = heroList.stream().map(hero -> hero.getName()).distinct().collect(Collectors.joining(","));
        System.out.println("joining2:" + joining2);
        String joining3 = heroList.stream().map(hero -> hero.getName()).collect(Collectors.joining(",", "^_^", "~~~"));
        System.out.println("joining3:" + joining3);


    }

    private static void filter(List<Hero> heroList, HeroChecker heroChecker) {
        /*for (Hero hero : heroList) {
            if (hero.getHp()>100 && hero.getDamage()<50) {
                System.out.print(hero);
            }
        }*/
        for (Hero hero : heroList) {
            if (heroChecker.test(hero)) {
                System.out.print(hero);
            }
        }

    }
}
